package fmi.chesstern.ui.gui;

import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;

import javax.swing.JOptionPane;
import javax.swing.JPanel;

import fmi.chesstern.game.BoardField;
import fmi.chesstern.game.GameBoardState;
import fmi.chesstern.game.events.GameStateChangedEvent;
import fmi.chesstern.game.events.GameStateChangedListener;
import fmi.chesstern.pieces.ChessPiece;
import fmi.chesstern.pieces.ChessPieceColor;
import fmi.events.EventDispatcher;

public class ChessBoard extends JPanel {
	private static final long serialVersionUID = -3294798510281469142L;
	
	private static final Color BRIGHT_COLOR = new Color(255, 206, 158);
	private static final Color BRIGHT_TARGET_COLOR = new Color(128, 103, 79);
	private static final Color DARK_COLOR = new Color(209, 139, 71);
	private static final Color DARK_TARGET_COLOR = new Color(105, 70, 36);
	private static final Color SELECTED_COLOR = new Color(238, 238, 238);
	private static final Color TEXT_COLOR = new Color(0, 0, 0);
	private static final Color BACKGROUND_COLOR = new Color(250, 250, 250);
	
	private GUIBoardState state;
	private GameBoardState gameBoardState;
	
	private EventDispatcher<FieldClickEvent, FieldClickListener> dispatcher;
	
	public void addFieldClickListener(FieldClickListener listener) {
		dispatcher.addEventListener(listener);
	}
	
	public void removeFieldClickListener(FieldClickListener listener) {
		dispatcher.removeEventListener(listener);
	}
	
	public ChessBoard(GUIBoardState state) {
		this.state = state;
		this.gameBoardState = state.getGameState().getGameBoardState();
		state.addGameStateChangedListener(new GameStateChangedListener() {
			@Override
			public void handleEvent(GameStateChangedEvent event) {
				repaint();
				
				if (gameBoardState.isInMate()) {
					String winningPlayer = gameBoardState.getCurrentPlayer() == ChessPieceColor.WHITE ? "BLACK" : "WHITE";
					JOptionPane.showMessageDialog(ChessBoard.this, winningPlayer + " win!", "In checkmate!", JOptionPane.INFORMATION_MESSAGE);
				}
			}
		});
		
		addMouseListener(new ClickHandler());
		
		dispatcher = new EventDispatcher<FieldClickEvent, FieldClickListener>();
	}
	
	private class ClickHandler extends MouseAdapter {
		@Override
		public void mouseClicked(MouseEvent e) {
			if (gameBoardState.isInMate()) {
				return;
			}
			int x = e.getX();
			int y = e.getY();
			
			int fieldSize = getFieldSize();
			int boardSize = gameBoardState.getBoardSize();
			
			if (x >= fieldSize && y >= fieldSize
					&& x <= (boardSize + 1) * fieldSize && y <= (boardSize + 1) * fieldSize) {
				int row = boardSize - y / fieldSize;
				int column = x / fieldSize - 1;
				
				dispatcher.dispatchEvent(new FieldClickEvent(ChessBoard.this, new BoardField(row, column)));
			}
		}
	}
	
	private int getFieldSize() {
		return Math.min(getSize().width, getSize().height) / (gameBoardState.getBoardSize() + 2);
	}	
	
	@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		
		setBackground(BACKGROUND_COLOR);
		
		int fieldSize = getFieldSize();
		
		g.setColor(TEXT_COLOR);

		g.drawRect(fieldSize - 1, fieldSize - 1, (gameBoardState.getBoardSize()) * fieldSize + 1, (gameBoardState.getBoardSize()) * fieldSize + 1);
		
		g.setFont(g.getFont().deriveFont(AffineTransform.getScaleInstance(fieldSize / 25.0, fieldSize / 25.0)));
		FontMetrics fontMetrics = g.getFontMetrics(g.getFont());
		int letterHeight = fontMetrics.getHeight();
		
		for (int i = 1; i <= gameBoardState.getBoardSize(); i++) {
			char character = (char) ('a' + i - 1);
			int x = fieldSize * i + fieldSize / 2 - fontMetrics.charWidth(character) / 2;
			int y = fieldSize / 2 + (letterHeight / 2);
			
			g.drawString("" + character, x, y);
			g.drawString("" + character, x, y + fieldSize * (gameBoardState.getBoardSize() + 1) - letterHeight / 2);
			
			character = (char) ('1' + gameBoardState.getBoardSize() - i);
			x = fieldSize / 2 - fontMetrics.charWidth(character) / 2;
			y = fieldSize * i + fieldSize / 2 + (letterHeight / 2);
			g.drawString("" + character, x, y);
			g.drawString("" + character, x + fieldSize * (gameBoardState.getBoardSize() + 1), y);
		}
		
		for (BoardField field : gameBoardState.getFields()) {
			if ((field.getRow() + field.getColumn()) % 2 == 0) {
				g.setColor(state.getTargetFields().contains(field) ? DARK_TARGET_COLOR: DARK_COLOR);
			} else {
				g.setColor(state.getTargetFields().contains(field) ? BRIGHT_TARGET_COLOR: BRIGHT_COLOR);
			}
			if (state.getSelectedField() != null && state.getSelectedField().equals(field)) {
				g.setColor(SELECTED_COLOR);
			}
			
			int x = (field.getColumn() + 1) * fieldSize;
			int y = (gameBoardState.getBoardSize() - field.getRow()) * fieldSize;
			g.fillRect(x, y, fieldSize, fieldSize);
			ChessPiece piece = gameBoardState.getPieceAt(field);
			if (piece != null) {
				g.drawImage(ChessPieceResourceLocator.getImageOf(piece), x, y, fieldSize, fieldSize, null);
			}
		}
	}
}
